﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Dou.UI;
using DouEditor.Utils;
using UnityEditor;
using UnityEngine;

namespace DouEditor.UI
{
    /// <summary>
    /// UGUI 富文本资源创建窗口
    /// </summary>
    public class RichTextTexturePackerWindow : EditorWindow
    {
        [MenuItem("DouFramework/UI/构建RichTextAsset")]
        public static void ShowWindow()
        {
            EditorWindow window = GetWindow<RichTextTexturePackerWindow>(false, "构建RichTextAsset");
            window.minSize = new Vector2(550, 400);
        }

        private const string SingleSprite = "^[0-9a-zA-Z]+$";
        private const string MultiSprite = "^([0-9a-zA-Z]+)_([0-9]+)$";
        
        private string _originPath = String.Empty;
        
        private GUIStyle _boxStyle;
        
        private void OnGUI()
        {
            if (_boxStyle == null)
            {
                _boxStyle = new GUIStyle(GUI.skin.box);
                _boxStyle.alignment = TextAnchor.MiddleLeft;
                _boxStyle.fontSize = 13;
                _boxStyle.richText = true;
            }
            
            using (new GUILayout.VerticalScope())
            {
                GUILayout.Box(new GUIContent("图集打包规范如下：\n*注意：当前版本只是验证可行性，所有精灵图片尺寸都必须一致且是正方形，同时图片必须位于项目中！*\n1. 图片命名只能包含数字英文字母大小写；\n如：happy.png;\n2. 针对序列帧动画，其规则是相同名称加下划线加序号来确定，序号不需要保证连续；\n如：happy_1.png; happy_10.png;\n3. 序列帧动画速度在生成后的资源文件中修改调整；"), _boxStyle, GUILayout.ExpandWidth(true), GUILayout.Height(150));
                using (new GUILayout.HorizontalScope())
                {
                    _originPath = EditorGUILayout.TextField(new GUIContent("待打包图片文件夹"), _originPath);
                    if (GUILayout.Button("选择", GUILayout.Width(80)))
                    {
                        var folder = EditorUtility.OpenFolderPanel("选择文件夹", Application.dataPath, "");
                        if (!string.IsNullOrEmpty(folder))
                        {
                            _originPath = folder;
                        }
                    }
                }
                if (GUILayout.Button("开始构建富文本资源"))
                {
                    var targetFile = EditorUtility.SaveFilePanelInProject("保存文件", "RichTextAsset", "asset", "");
                    if (!string.IsNullOrEmpty(targetFile))
                    {
                        PackerAsset(_originPath, targetFile);
                    }
                }
            }
        }

        private void PackerAsset(string originPath, string targetFile)
        {
            var files = Directory.GetFiles(originPath, "*.*", SearchOption.TopDirectoryOnly)
                .Where(s => s.EndsWith(".png") || s.EndsWith(".jpg"));

            var spriteCount = 0;
            var filePath = string.Empty;
            var singleSprites = new List<SpriteInfo>();
            var multiSprites = new Dictionary<string, List<SpriteInfo>>();
            
            foreach (var file in files)
            {
                var fileName = Path.GetFileNameWithoutExtension(file);
                var singleMatch = Regex.Match(fileName, SingleSprite);
                if (singleMatch.Success)
                {
                    singleSprites.Add(new SpriteInfo
                    {
                        spriteName = fileName,
                        filePath = file
                    });
                    spriteCount++;
                    filePath = file;
                    continue;
                }
                var multiMatch = Regex.Match(fileName, MultiSprite);
                if (multiMatch.Success)
                {
                    var spriteName = multiMatch.Groups[1].Value;
                    var spriteNum = int.Parse(multiMatch.Groups[2].Value);
                    if (!multiSprites.ContainsKey(spriteName))
                    {
                        multiSprites.Add(spriteName, new List<SpriteInfo>());
                    }
                    multiSprites[spriteName].Add(new SpriteInfo
                    {
                        spriteName = spriteName,
                        num = spriteNum,
                        filePath = file
                    });
                    spriteCount++;
                    filePath = file;
                    continue;
                }
            }

            foreach (var kv in multiSprites)
            {
                var list = kv.Value;
                list.Sort((a, b) => a.num - b.num);
            }

            if (spriteCount == 0)
            {
                return;
            }
            
            var size = Vector2Int.zero;
            {
                var texture = AssetDatabase.LoadAssetAtPath<Texture>(filePath);
                size.Set(texture.width, texture.height);
            }

            var dir = Path.GetDirectoryName(targetFile);
            var tFileName = Path.GetFileNameWithoutExtension(targetFile);
            var column = Mathf.CeilToInt(Mathf.Sqrt(spriteCount));
            var sheetSize = size * column;
            
            var asset = ScriptableObject.CreateInstance<RichTextAsset>();
            asset.spriteSize = (float) size.x / (float) sheetSize.x;
            asset.column = column;
            CreateSpriteSheet(singleSprites, multiSprites, column, size, sheetSize, dir, tFileName, out asset.spriteSheet, out asset.spriteInfoList);
            asset.material = CreateMaterial(asset, dir, tFileName);
            
            AssetDatabase.CreateAsset(asset, targetFile);
            AssetDatabase.Refresh();

            EditorUtility.DisplayDialog("提示", "生成成功！", "确定");
        }

        private void CreateSpriteSheet(List<SpriteInfo> singleSprites, Dictionary<string,List<SpriteInfo>> multiSprites, in int column, Vector2Int size, Vector2Int sheetSize, string dir, string fileName, out Texture2D spriteSheet, out List<SpriteData> spriteInfoList)
        {
            spriteSheet = new Texture2D(sheetSize.x, sheetSize.y, TextureFormat.RGBA32, false);
            for (int i = 0; i < sheetSize.x; i++)
            {
                for (int j = 0; j < sheetSize.y; j++)
                {
                    spriteSheet.SetPixel(i, j, Color.clear);
                }
            }

            spriteInfoList = new List<SpriteData>();

            var index = 0;
            var spriteList = new List<Texture2D>();
            
            foreach (var spriteInfo in singleSprites)
            {
                spriteInfoList.Add(new SpriteData
                {
                    name = spriteInfo.spriteName,
                    index = index++,
                    frameCount = 1,
                    speed = 1
                });
                spriteList.Add(AssetDatabase.LoadAssetAtPath<Texture2D>(spriteInfo.filePath));
            }
            foreach (var kv in multiSprites)
            {
                var spriteName = kv.Key;
                var list = kv.Value;
                spriteInfoList.Add(new SpriteData
                {
                    name = spriteName,
                    index = index,
                    frameCount = list.Count,
                    speed = 1
                });
                index += list.Count;
                foreach (var spriteInfo in list)
                {
                    spriteList.Add(AssetDatabase.LoadAssetAtPath<Texture2D>(spriteInfo.filePath));
                }
            }
            
            // 绘制到图集
            for (int i = 0; i < spriteList.Count; i++)
            {
                var col = i % column;
                var row = i / column;
                var x = col * size.x;
                var y = row * size.y;
                spriteSheet.SetPixels(x, y, size.x, size.y, spriteList[i].GetPixels());
            }
            
            spriteSheet.Apply();
            byte[] bytes = spriteSheet.EncodeToPNG();

            var path = Path.Combine(dir, fileName + ".png");
            EditorUtil.CreateDirByFilePath(path);
            File.WriteAllBytes(path, bytes);
            AssetDatabase.ImportAsset(path);
            
            var importer = (TextureImporter) TextureImporter.GetAtPath(path);
            importer.alphaIsTransparency = true;
            importer.npotScale = TextureImporterNPOTScale.None;
            importer.mipmapEnabled = false;
            importer.isReadable = false;
            importer.wrapMode = TextureWrapMode.Clamp;
            importer.maxTextureSize = 4096;
            var settings = importer.GetDefaultPlatformTextureSettings();
            settings.format = TextureImporterFormat.RGBA32;
            importer.SetPlatformTextureSettings(settings);
            importer.SaveAndReimport();
        }

        private Material CreateMaterial(RichTextAsset asset, string dir, string fileName)
        {
            var material = new Material(Shader.Find("Dou/UIRichText"));
            
            material.SetTexture("_SpriteSheetTex", asset.spriteSheet);
            material.SetFloat("_SpriteSize", asset.spriteSize);
            material.SetFloat("_Column", asset.column);
            
            AssetDatabase.CreateAsset(material, Path.Combine(dir, fileName + ".mat"));
            AssetDatabase.Refresh();
            
            return material;
        }

        private class SpriteInfo
        {
            public string spriteName;
            public int num;
            public string filePath;
        }
    }
}
